package com.kinyx.framework.jexl;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import com.kinyx.framework.annotation.RandomExpression;
import com.kinyx.framework.annotation.RandomIgnore;
import com.kinyx.framework.annotation.assist.CanNotNull;
import com.kinyx.framework.annotation.assist.ReturnNotNull;
import com.kinyx.framework.annotation.assist.ReturnNull;
import com.kinyx.framework.annotation.assist.Tester;
import com.kinyx.framework.annotation.assist.Throws;
import com.kinyx.framework.business.KRandomUtils;
import com.kinyx.framework.exception.KRuntimeException;
import com.kinyx.framework.reflect.KAnnotationUtils;
import com.kinyx.framework.reflect.KBeanUtils;
import com.kinyx.framework.reflect.KFieldUtils;
import com.kinyx.framework.utils.KStringUtils;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * 随机工具类
 */
@Tester("com.kinyx.framework.jexl.KRandomUtils2_Tester_")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class KRandomUtils2 {

	@ReturnNull("未标注@Random注解的expression值，且取不到默认值。")
	private static String getRandomExpression(@CanNotNull final Field field) {
		final RandomExpression annotation = field.getAnnotation(RandomExpression.class);// 取随机数据配置注解
		if (annotation != null) {// 有注解且配置了注解值，则直接返回配置值。
			final String expression = annotation.value();
			if (!KStringUtils.isBlank(expression)) {
				return expression;
			}
		}

		// 取字段类型对应的默认随机表达式
		final String mapping = RandomMappings.expressions.get(field.getType());
		// 未配置返回null
		return mapping == null ? null : mapping;
	}

	/**
	 * 添加映射参数
	 * @param key 键
	 * @param param 参数
	 */
	public static void addMappingParam(final String key, final Object param) {
		RandomMappings.params.put(key, param);
	}

	/**
	 * 添加默认随机表达式
	 * @param clazz 类型
	 * @param expression 随机表达式
	 */
	public static void addDefaultExpression(final Class<?> clazz, final String expression) {
		RandomMappings.expressions.put(clazz, expression);
	}

	private static Object randomValue(final Field field, final String randomExpression, final int times) {
		if (times > 333) { throw new KRuntimeException(new IllegalArgumentException("cannot create random non null value, already tried " + (times - 1) + " times, it looks like to lead to and infinite loop.")); }
		if (randomExpression != null) {// 有随机配置则按照随机配置执行
			return KJexlUtils.evaluate(RandomMappings.params, randomExpression);
		}
		// 无随机配置
		final Class<?> type = field.getType();
		if (type.isEnum()) {// 枚举类型特殊处理
			if (KRandomUtils.randomBoolean(false)) {
				final Object[] enumConstants = type.getEnumConstants();
				return enumConstants[KRandomUtils.random(0, enumConstants.length - 1)];
			}
		}
		// 无法处理的类型，或随机未命中时，返回null。
		return null;
	}

	/**
	 * 随机对象
	 * @param <T> 泛型
	 * @param clazz 类型
	 * @param nullablePercentage 出现null值的百分比。 0：表示不出现null值；100：表示只出现null值；其它值：百分比概率出现null值；
	 * @return 对象
	 */
	@ReturnNull("nullable == true时随机值可能为null")
	@Throws(at = "KFieldUtils.set(Object, Field, Object)", value = "IllegalArgumentException", when = "Field.set(Object, Object)")
	//	@Throws(at = "KFieldUtils.set(Object, Field, Object)", value = "IllegalAccessException", when = "Field.set(Object, Object)")
	@Throws(at = "KBeanUtils.newInstance(Class, Class<?>[], Object[])", value = "NoSuchMethodException", when = "Class.getDeclaredConstructor((Class...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Class<?>[], Object[])", value = "SecurityException", when = "Class.getDeclaredConstructor((Class...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Class<?>[], Object[])", value = "InstantiationException", when = "Constructor.newInstance(Object...)")
	//	@Throws(at = "KBeanUtils.newInstance(Class, Class<?>[], Object[])", value = "IllegalAccessException", when = "Constructor.newInstance(Object...)")
	//	@Throws(at = "KBeanUtils.newInstance(Class, Class<?>[], Object[])", value = "IllegalArgumentException", when = "Constructor.newInstance(Object...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Class<?>[], Object[])", value = "InvocationTargetException", when = "Constructor.newInstance(Object...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Param<?>...)", value = "NoSuchMethodException", when = "Class.getDeclaredConstructor((Class...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Param<?>...)", value = "SecurityException", when = "Class.getDeclaredConstructor((Class...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Param<?>...)", value = "InstantiationException", when = "Constructor.newInstance(Object...)")
	//	@Throws(at = "KBeanUtils.newInstance(Class, Param<?>...)", value = "IllegalAccessException", when = "Constructor.newInstance(Object...)")
	//	@Throws(at = "KBeanUtils.newInstance(Class, Param<?>...)", value = "IllegalArgumentException", when = "Constructor.newInstance(Object...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Param<?>...)", value = "InvocationTargetException", when = "Constructor.newInstance(Object...)")
	public static <T> T randomBean(@CanNotNull final Class<T> clazz, @CanNotNull final int nullablePercentage) {
		if (nullablePercentage == 100) { return null; } // 百分之百为null
		if ((nullablePercentage != 0) && (KRandomUtils.random(1, 100) <= nullablePercentage)) { return null; } // 可为空且随机命中，则返回null对象。
		final T bean = KBeanUtils.newInstance(clazz);// 创建对象
		// 对所有字段进行赋值
		KFieldUtils.getFieldList(clazz, true, false, false).forEach(field -> {
			// 必null注解
			final RandomIgnore $RandomIgnore = field.getAnnotation(RandomIgnore.class);
			if ($RandomIgnore != null) { return; } // 忽略该字段的随机值处理

			// 必null注解
			final Annotation Null = KAnnotationUtils.get(field, "javax.validation.constraints.Null");
			if (Null != null) { return; } // 不处理该字段值，即设置为null。

			// 执行表达式得到随机值
			final String randomExpression = getRandomExpression(field);
			Object value = randomValue(field, randomExpression, 0);
			// 非null注解
			final Annotation notNull = KAnnotationUtils.get(field, "javax.validation.constraints.NotNull");
			if (notNull != null) {
				// 无限循环必须取到值，此处可能因配置问题导致永远无法取到非null值，因而限定循环次数，超过次数则报错。
				int times = 0;
				if (value == null) {
					while (value != null) { value = randomValue(field, randomExpression, times++); }
				}
			}
			// 无结果
			if (value == null) { return; } // 不处理该字段值，即设置为null。
			// 有结果
			KFieldUtils.set(bean, field, value);// 设置值
		});
		return bean;
	}

	/**
	 * 随机对象列表
	 * @param <T> 泛型
	 * @param clazz 类型
	 * @param nullablePercentage 出现null值的百分比。 0：表示不出现null值；100：表示只出现null值；其它值：百分比概率出现null值；
	 * @param minCnt 最小数量
	 * @param maxCnt 最大数量
	 * @return 对象列表
	 */
	@ReturnNotNull
	@Throws(at = "KFieldUtils.set(Object, Field, Object)", value = "IllegalArgumentException", when = "Field.set(Object, Object)")
	//	@Throws(at = "KFieldUtils.set(Object, Field, Object)", value = "IllegalAccessException", when = "Field.set(Object, Object)")
	@Throws(at = "KBeanUtils.newInstance(Class, Class<?>[], Object[])", value = "NoSuchMethodException", when = "Class.getDeclaredConstructor((Class...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Class<?>[], Object[])", value = "SecurityException", when = "Class.getDeclaredConstructor((Class...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Class<?>[], Object[])", value = "InstantiationException", when = "Constructor.newInstance(Object...)")
	//	@Throws(at = "KBeanUtils.newInstance(Class, Class<?>[], Object[])", value = "IllegalAccessException", when = "Constructor.newInstance(Object...)")
	//	@Throws(at = "KBeanUtils.newInstance(Class, Class<?>[], Object[])", value = "IllegalArgumentException", when = "Constructor.newInstance(Object...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Class<?>[], Object[])", value = "InvocationTargetException", when = "Constructor.newInstance(Object...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Param<?>...)", value = "NoSuchMethodException", when = "Class.getDeclaredConstructor((Class...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Param<?>...)", value = "SecurityException", when = "Class.getDeclaredConstructor((Class...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Param<?>...)", value = "InstantiationException", when = "Constructor.newInstance(Object...)")
	//	@Throws(at = "KBeanUtils.newInstance(Class, Param<?>...)", value = "IllegalAccessException", when = "Constructor.newInstance(Object...)")
	//	@Throws(at = "KBeanUtils.newInstance(Class, Param<?>...)", value = "IllegalArgumentException", when = "Constructor.newInstance(Object...)")
	@Throws(at = "KBeanUtils.newInstance(Class, Param<?>...)", value = "InvocationTargetException", when = "Constructor.newInstance(Object...)")
	public static <T> List<T> randomBeanList(@CanNotNull final Class<T> clazz, @CanNotNull final int nullablePercentage, final int minCnt, final int maxCnt) {
		final int total = KRandomUtils.random(minCnt, maxCnt);
		final List<T> beans = new ArrayList<>();
		for (int i = 0; i < total; i++) {
			beans.add(randomBean(clazz, nullablePercentage));
		}
		return beans;
	}

}
